<?php
namespace JsonLowCode\Table;

use Closure;
use JsonLowCode\Schema;
use JsonLowCode\Factory\ColumnFactory as Factory;
use JsonLowCode\Table;

/**
 * @method Column\Rate rate(int $count = 5, ?Closure $closure = null) 评分组件渲染
 * @method Column\Tag tag(string $type = 'default', ?Closure $closure = null) 单标签组件渲染
 * @method Column\Button button(string $type = 'default', ?Closure $closure = null) 评分组件渲染
 * @method Column\Icon icon(int|string $size = 24, ?Closure $closure = null) 图标组件渲染
 */
class Column extends Factory
{
    /**
     * 表组件
     * @var Table
     */
    protected Table $table;
    /**
     * 子组件元素
     * @var Schema|null
     */
    protected ?Schema $element = null;

    /**
     * 字段映射集合
     * @var array
     */
    protected array $maps = [];
    /**
     * 颜色映射集合
     * @var array
     */
    protected array $colors = [];
    /**
     * 是否默认选中
     * @var bool
     */
    public bool $checked = false;
    /**
     * 是否默认展开
     * @var bool
     */
    public bool $expanded = false;

    public string $rowDataField = "self.rowData";
    public string $rowDataIndex = "self.rowData";

    /**
     * @param string $key
     * @param string $title
     * @param Table|null $table
     */
    public function __construct(string $key = "", string $title = "", Table $table = null)
    {
        parent::__construct();
        $this->table = $table;
        list($rowDataField,$rowDataIndex) = $this->table->getRowData();
        $this->rowDataField = $rowDataField;
        $this->rowDataIndex = $rowDataIndex;
        $this->key($key);
        $title && $this->title($title);
    }

    /**
     * 设置默认选中字段
     * @param bool $checked
     * @return $this
     */
    public function checked(bool $checked = true): static
    {
        $this->checked = $checked;
        return $this;
    }

    /**
     * 设置默认展开字段
     * @param bool $expanded
     * @return $this
     */
    public function expanded(bool $expanded = true): static
    {
        $this->expanded = $expanded;
        return $this;
    }

    /**
     * 设置字段映射回调集合
     * @param Closure $closure
     * @return $this
     */
    public function display(Closure $closure): static
    {
        $field = $this->getField();
        $this->displays[$field] = $closure;
        return $this;
    }

    /**
     * 获取回调集合
     * @param string|null $key
     * @return array<string,Closure>|Closure
     */
    public function displays(string $key = null): array|Closure
    {
        if ($key!=null){
            return $this->displays[$key];
        }else{
            return $this->displays;
        }
    }

    /**
     * 设置映射
     * @return $this
     */
    public function maps(array $maps = []): static
    {
        $this->maps = $maps;
        return $this;
    }

    /**
     * 获取映射组
     * @return array
     */
    public function getMaps(): array
    {
        return $this->maps;
    }

    /**
     * 是否映射字段
     * @return bool
     */
    public function isMap(): bool
    {
        return !empty($this->maps);
    }

    /**
     * 设置颜色映射
     * @param array $colors
     * @return $this
     */
    public function colors(array $colors = []): static
    {
        $this->colors = $colors;
        return $this;
    }

    /**
     * 获取颜色组
     * @return array
     */
    public function getColors(): array
    {
        return $this->colors;
    }

    /**
     * 是否映射颜色
     * @return bool
     */
    public function isColor(): bool
    {
        return !empty($this->colors);
    }

    /**
     * 是否render渲染
     * @return bool
     */
    public function isRender(): bool
    {
        if (isset($this->attributes['type'])){
            return $this->attributes['type']=='render';
        }
        return false;
    }

    /**
     * 设置type类型
     * @param string $type
     * @param bool $isJSExpression 是否js表达式
     * @return $this
     */
    protected function type(string $type, bool $isJSExpression = false): static
    {
        return $this->setAttribute('type',$type,$isJSExpression);
    }

    /**
     * 设置为多选类型
     * @param string|bool $isMultiple true=多选, false=单选
     * @param bool $isJSExpression 是否js表达式
     * @return $this
     */
    public function selection(string|bool $isMultiple = true, bool $isJSExpression = false): static
    {
        $this->type("selection");
        $this->setAttribute("multiple",$isMultiple, $isJSExpression);
        return $this;
    }

    /**
     * 设置展开类型
     * @return $this
     */
    public function expand(): static
    {
        $this->type("expand");
        return $this;
    }

    /**
     * 设置是否显示
     * @param bool $show
     * @return $this
     */
    public function show(bool $show = true): static
    {
        return $this->setAttribute('show',$show);
    }

    /**
     * 是否循环组件
     * @param bool $bool
     * @return $this
     */
    public function isLoop(bool $bool = true): static
    {
        return $this->setAttribute('isLoop',$bool);
    }


    /**
     * @return $this
     */
    public function image($props = []): static
    {
        $this->type("image");
        $this->attributes['props'] = $props;
        return $this;
    }


    /**
     * @param string $key
     * @param bool $isJSExpression 是否js表达式
     * @return $this
     */
    public function key(string $key, bool $isJSExpression = false): static
    {
        return $this->setAttribute('key',$key,$isJSExpression);
    }

    /**
     * 设置属性标题
     * @param string $title
     * @param bool $isJSExpression 是否js表达式
     * @return $this
     */
    public function title(string $title, bool $isJSExpression = false): static
    {
        return $this->setAttribute('title',$title,$isJSExpression);
    }

    /**
     * 设置属性标题
     * @param string|int $span title 行所占的单元格的个数
     * @param bool $isJSExpression 是否js表达式
     * @return $this
     */
    public function titleRowSpan(string|int $span, bool $isJSExpression = false): static
    {
        return $this->setAttribute('titleRowSpan',$span,$isJSExpression);
    }



    /**
     * @param string|bool $tree
     * @param bool $isJSExpression 是否js表达式
     * @return $this
     */
    public function tree(string|bool $tree = true, bool $isJSExpression = false): static
    {
        return $this->setAttribute('tree',$tree,$isJSExpression);
    }

    /**
     * 列宽是否可以拖动
     * @param string|bool $resizable
     * @param bool $isJSExpression 是否js表达式
     * @return $this
     */
    public function resizable(string|bool $resizable = true, bool $isJSExpression = false): static
    {
        return $this->setAttribute('resizable',$resizable,$isJSExpression);
    }

    /**
     * 文本溢出的设置省略
     * @param string|bool|array $ellipsis EllipsisProps
     * @param bool $isJSExpression 是否js表达式
     * @return $this
     */
    public function ellipsis(string|bool|array $ellipsis = true, bool $isJSExpression = false): static
    {
        return $this->setAttribute('ellipsis',$ellipsis,$isJSExpression);
    }

    /**
     * 文本 tooltip 省略提示
     * @param int|string $width
     * @param array $props
     * @return $this
     */
    public function tooltip(int|string $width = 60, array $props = []): static
    {
        return $this->setAttribute('ellipsis',array_merge($props,[
            "tooltip"=> true
        ]))->width($width);
    }

    /**
     * 列内的文本排列
     * @param string $align 'left' | 'right' | 'center'
     * @return $this
     */
    public function align(string $align = 'left' | 'right' | 'center'): static
    {
        return $this->setAttribute('align',$align);
    }

    /**
     * 该列是否需要固定
     * @param bool|string $fixed 'left' | 'right' | false
     * @return $this
     */
    public function fixed(bool|string $fixed = false): static
    {
        return $this->setAttribute('fixed',$fixed);
    }

    /**
     * 该列是否需要固定左侧
     * @return $this
     */
    public function fixedLeft(): static
    {
        return $this->fixed('left');
    }

    /**
     * 该列是否需要固定左侧
     * @return $this
     */
    public function fixedRight(): static
    {
        return $this->fixed('right');
    }


    /**
     * 列的宽度（在列固定时是必需的，并且需要为 number 类型）
     * @param int $width
     * @return $this
     */
    public function width(int $width): static
    {
        return $this->setAttribute('width',$width);
    }

    /**
     * 列的最小宽度
     * @param int $minWidth
     * @return $this
     */
    public function minWidth(string|int $minWidth): static
    {
        return $this->setAttribute('minWidth',$minWidth);
    }

    /**
     * 列的最大宽度，仅在 resizable 为 true 的时候生效
     * @param int $maxWidth
     * @return $this
     */
    public function maxWidth(int $maxWidth): static
    {
        return $this->setAttribute('maxWidth',$maxWidth);
    }

    /**
     * 列的类名
     * @param string $className
     * @return $this
     */
    public function className(string $className): static
    {
        return $this->setAttribute('className',$className);
    }

    /**
     * 成组列头的子节点
     * @param array $children
     * @return $this
     */
    public function children(array $children): static
    {
        return $this->setAttribute('children',$children);
    }

    /**
     * 该列单元格的 HTML 属性
     * @param string $event (rowData: object, rowIndex: number) => object
     * @return $this
     */
    public function cellProps(string $event): static
    {
        return $this->setEventAttribute('cellProps',$event);
    }

    /**
     * 该列单元格的的 col span
     * @param string $event (rowData: object, rowIndex: number) => number
     * @return $this
     */
    public function colSpan(string $event): static
    {
        return $this->setEventAttribute('colSpan',$event);
    }

    /**
     * 是否禁用 只作用于 type=selection
     * @param string $event (rowData: object, rowIndex: number) => boolean
     * @return $this
     */
    public function disabled(string $event): static
    {
        return $this->setEventAttribute('disabled',$event);
    }

    /**
     * 行是否可展开，仅在 type 为 'expand' 时生效
     * @param string $event (rowData: object) => boolean
     * @return $this
     */
    public function expandable(string $event): static
    {
        return $this->setEventAttribute('expandable',$event);
    }


    /**
     * 这一列的排序方法。如果设为 'default' 表格将会使用一个内置的排序函数；如果设为 true，
     * 表格将只会在这列展示一个排序图标，在异步的时候可能有用。其他情况下它工作的方式类似 Array.sort 的对比函数
     * @param string|bool $sorter boolean | function | 'default'
     * @return $this
     */
    public function sorter(string|bool $sorter): static
    {
        return $this->setAttribute('sorter',$sorter);
    }

    /**
     * 受控状态下表格的排序方式。如果多列都设定了有效值，那么只有第一个会生效
     * @param string|bool $sortOrder 'descend' | 'ascend' | false
     * @return $this
     */
    public function sortOrder(string|bool $sortOrder): static
    {
        return $this->setAttribute('sortOrder',$sortOrder);
    }


    /**
     * 渲染使用 this.rowData js对象访问当前行数据, this.rowIndex 当前行索引
     * @param mixed|null $schema (rowData: object, rowIndex: number) => VNodeChild
     * @return $this
     */
    public function render(mixed $schema = null): static
    {
        $this->type("render");
        return $this->vueRender($schema);
    }

    /**
     * 展开区域的渲染函数，仅在 type 为 'expand' 的时候生效,渲染使用 this.rowData js对象访问当前行数据, this.rowIndex 当前行索引
     * @param mixed|null $schema (rowData: object, rowIndex: number) => VNodeChild
     * @return $this
     */
    public function renderExpand(mixed $schema = null): static
    {
        return $this->setAttribute('renderExpandOptions',JSSlot($schema,['rowData','index']));
    }

    /**
     * @param mixed|null $schema
     * @return $this
     */
    protected function vueRender(mixed $schema = null): static
    {
        return $this->setAttribute('render',JSSlot($schema,['rowData','index']));
    }

    /**
     * @return string
     */
    public function getField(): string
    {
        return $this->attributes['key'];
    }

    /**
     * 设置attribute属性
     * @param string $key 键
     * @param mixed $value 值
     * @param bool $isJSExpression bool $isJSExpression 是否js表达式
     * @return $this
     */
    public function setAttribute(string $key, mixed $value, bool $isJSExpression = false): static
    {
        $this->attributes[$key] = $isJSExpression?JSExpression($value):$value;
        return $this;
    }

    /**
     * 设置attribute属性
     * @param string $key 键
     * @param mixed $value 值
     * @param bool $isJSExpression bool $isJSExpression 是否js表达式
     * @return $this
     */
    public function setEventAttribute(string $key, mixed $value, bool $isJSExpression = false): static
    {
        return $this->setAttribute($key,$isJSExpression?JSExpression($value):JSFunction($value));
    }

    public function toArray(): array
    {
        if ($this->element){
            $this->render($this->element);
        }
        if ($this->iconElement){
            $this->attributes['props'] = $this->iconElement->getProps();
        }
        return $this->attributes;
    }
}
